#include "stdafx.h"
#include <MyViews/PanView.h>
#include <MyViews/Resource.h>

const int SCROLL_LINE = 5;

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

#pragma warning(push)
#pragma warning(disable: 4355) // 'this' used in base member initializer list

PanView::PanView()
	: m_ToolPan(* this)
{
	m_Gap = 0;
	m_Size = m_BenchSize = CSize(0, 0);
	m_Origin = CPoint(0, 0);
	m_BackgroundColor = GetSysColor(COLOR_APPWORKSPACE);
}

#pragma warning(pop)

PanView::~PanView()
{
	SetActiveTool(0);
}

CSize PanView::GetClientSize() const 
{
	CRect r;
	GetClientRect(&r);
	return r.Size();
}				

CPoint PanView::ClientToBench(CPoint p) const
{
	p += m_Origin;
	p.x -= m_Gap;
	p.y -= m_Gap;
	return p;
}

CPoint PanView::BenchToClient(CPoint p) const
{
	p.x += m_Gap;
	p.y += m_Gap;
	p -= m_Origin;
	return p;
}

CRect PanView::ClientToBench(CRect r) const
{
	r.OffsetRect(m_Origin);
	r.OffsetRect(- m_Gap, - m_Gap);
	return r;
}

CRect PanView::BenchToClient(CRect r) const
{
	r.OffsetRect(m_Gap, m_Gap);
	r.OffsetRect(- m_Origin);
	return r;
}

CRect PanView::BenchRect() const
{
	return BenchToClient(CRect(
		CPoint(0, 0), CPoint(m_BenchSize.cx, m_BenchSize.cy)));
}	   

void PanView::SetGap(int gap)
{
	if (gap != m_Gap) {
		m_Gap = gap;
		m_Size = m_BenchSize;

		m_Size.cx += 2 * m_Gap;
		m_Size.cy += 2 * m_Gap;

		UpdateScrollBars();
		SetOrigin(m_Origin);
		if (m_hWnd != 0)
			Invalidate(FALSE);
	}
}

void PanView::SetOrigin(CPoint p, bool bInvalidate)
{
	CSize s(0, 0);
	if (m_hWnd != 0)
		s = GetClientSize();
	if (s.cx > m_Size.cx)
		p.x = (m_Size.cx - s.cx) / 2;
	else {
		if (p.x < 0)
			p.x = 0;
		if (p.x > m_Size.cx - s.cx)
			p.x = m_Size.cx - s.cx;
	}
	if (s.cy > m_Size.cy)
		p.y = (m_Size.cy - s.cy) / 2;
	else {
		if (p.y < 0)
			p.y = 0;
		if (p.y > m_Size.cy - s.cy)
			p.y = m_Size.cy - s.cy;
	}
	if (p != m_Origin) {
		CPoint dp = m_Origin - p;
		m_Origin = p;
		UpdateScrollBars();
		if (m_hWnd != 0) {
			if (bInvalidate)
				Invalidate(FALSE);
			else
				ScrollWindow(dp.x, dp.y);
		}
		OnPan();
	}
}

void PanView::SetBackgroundColor(COLORREF c)
{
	if (c != m_BackgroundColor) {
		m_BackgroundColor = c;
		InvalidateBorder();
	}
}


void PanView::ResizeBench(CSize s)
{
	if (s.cx < 0)
		s.cx = 0;
	if (s.cy < 0)
		s.cy = 0;
	if (s != m_BenchSize) 
	{
		m_BenchSize = s;
		m_Size = m_BenchSize;

		m_Size.cx += 2 * m_Gap;
		m_Size.cy += 2 * m_Gap;

		UpdateScrollBars();
		SetOrigin(m_Origin, true);
		if (m_hWnd != 0)
			Invalidate(FALSE);
		OnResizeBench();
	}
}

void PanView::UpdateScrollBars()
{
	if (m_hWnd == 0)
		return;

	SCROLLINFO info;
	info.cbSize = sizeof(SCROLLINFO);

	CSize s = GetClientSize();

	int cx = GetSystemMetrics(SM_CYHSCROLL);
	int cy = GetSystemMetrics(SM_CXVSCROLL);

	GetScrollInfo(SB_HORZ, &info);
	if (int(info.nPage) < info.nMax - info.nMin)
		s.cy += cy;
	GetScrollInfo(SB_VERT, &info);
	if (int(info.nPage) < info.nMax - info.nMin)
		s.cx += cx;

	if (m_Size.cx > s.cx)
		s.cy -= cy;
	if (m_Size.cy > s.cy)
		s.cx -= cx;

	if (m_Size.cx == s.cx)
		s.cx++;
	if (m_Size.cy == s.cy)
		s.cy++;

	info.fMask = SIF_PAGE|SIF_POS|SIF_RANGE;
	info.nMin	= 0;
	info.nMax	= m_Size.cx;
	info.nPage	= s.cx;
	info.nPos	= m_Origin.x;
	PreSetScrollInfo(SB_HORZ, &info);
	SetScrollInfo(SB_HORZ, &info, TRUE);

	info.fMask = SIF_PAGE|SIF_POS|SIF_RANGE;
	info.nMin	= 0;
	info.nMax	= m_Size.cy;
	info.nPage	= s.cy;
	info.nPos	= m_Origin.y;
	PreSetScrollInfo(SB_VERT, &info);
	SetScrollInfo(SB_VERT, &info, TRUE);
}

int PanView::HandleCreate(LPCREATESTRUCT lpCreateStruct) 
{
	if (OffscreenView::HandleCreate(lpCreateStruct) == -1)
		return -1;
	OnSize(0, 0, 0);
	return 0;
}

void PanView::HandleSize(UINT nType, int cx, int cy) 
{
	OffscreenView::HandleSize(nType, cx, cy);

	SetOrigin(m_Origin);

	UpdateScrollBars();	
}

void PanView::HandleHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar) 
{
	CSize s = GetClientSize();
	CPoint p = m_Origin;

	switch (nSBCode) {
	case SB_LEFT:
		p.x = 0;
		break;
	case SB_ENDSCROLL:
		break;
	case SB_LINELEFT:
		p.x -= SCROLL_LINE;
		break;
	case SB_LINERIGHT:
		p.x += SCROLL_LINE;
		break;
	case SB_PAGELEFT:
		p.x -= s.cx;
		break;
	case SB_PAGERIGHT:
		p.x += s.cx;
		break;
	case SB_RIGHT:
		p.x = m_Size.cx - s.cx;
		break;
	case SB_THUMBPOSITION:
	case SB_THUMBTRACK:
		p.x = nPos;
		break;
	default:
		OffscreenView::HandleHScroll(nSBCode, nPos, pScrollBar);
		return;
	}
	SetOrigin(p);
}

void PanView::HandleVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar) 
{
	CSize s = GetClientSize();
	CPoint p = m_Origin;

	switch (nSBCode) {
	case SB_TOP:
		p.y = 0;
		break;
	case SB_ENDSCROLL:
		break;
	case SB_LINEUP:
		p.y -= SCROLL_LINE;
		break;
	case SB_LINEDOWN:
		p.y += SCROLL_LINE;
		break;
	case SB_PAGEUP:
		p.y -= s.cy;
		break;
	case SB_PAGEDOWN:
		p.y += s.cy;
		break;
	case SB_BOTTOM:
		p.y = m_Size.cy - s.cy;
		break;
	case SB_THUMBPOSITION:
	case SB_THUMBTRACK:
		p.y = nPos;
		break;
	default:
		OffscreenView::HandleVScroll(nSBCode, nPos, pScrollBar);
		return;
	}
	SetOrigin(p);
}

void PanView::EraseBackground(CDC * pDC, const CRgn & rgn)
{
	CBrush bGround(GetBackgroundColor());

	CRgn paintRgn; 
	CRgn benchRgn;
	VERIFY(paintRgn.CreateRectRgn(0, 0, 0, 0));
	VERIFY(benchRgn.CreateRectRgnIndirect(&BenchRect()));
	VERIFY(paintRgn.CombineRgn(const_cast<CRgn *>(&rgn), &benchRgn, RGN_DIFF));

	VERIFY(pDC->FillRgn(&paintRgn, &bGround));
}

void PanView::DoPaint(CDC * pDC, const CRgn & updateRgn)
{
	CRect updateRect;
	updateRgn.GetRgnBox(&updateRect);

	EraseBackground(pDC, updateRgn);
	if (!AllowPaintOnBackground())
		pDC->IntersectClipRect(BenchRect());

//	CRect BenchUpdateRect;
//	if (BenchUpdateRect.IntersectRect(updateRect, BenchRect())) {
	PaintRect(pDC, updateRect/*BenchUpdateRect*/);
//	}
	if (GetManipulator() != 0)
		GetManipulator()->PaintRect(pDC, updateRect);
	else if (GetCurrentTool() != 0)
		GetCurrentTool()->PaintRect(pDC, updateRect);
}

void PanView::InvalidateBench()
{
	InvalidateRect(&BenchRect(), FALSE);
}

void PanView::InvalidateBorder()
{
	CRect rClient;
	GetClientRect(&rClient);

	CRgn borderRgn;
	CRgn benchRgn;

	VERIFY(borderRgn.CreateRectRgnIndirect(rClient));
	VERIFY(benchRgn.CreateRectRgnIndirect(&BenchRect()));
	VERIFY(borderRgn.CombineRgn(&borderRgn, &benchRgn, RGN_DIFF));

	InvalidateRgn(&borderRgn, FALSE);
}

BOOL PanView::PreCreateWindow(CREATESTRUCT& cs) 
{
	if (!OffscreenView::PreCreateWindow(cs))
		return FALSE;

	cs.style |= (WS_HSCROLL|WS_VSCROLL);

	return TRUE;
}
BEGIN_MESSAGE_MAP(PanView, OffscreenView)
	ON_COMMAND(ID_TOOL_PAN, OnToolPan)
	ON_UPDATE_COMMAND_UI(ID_TOOL_PAN, OnUpdateToolPan)
END_MESSAGE_MAP()

void PanView::OnToolPan()
{
	SetActiveTool(GetPanTool());
}

void PanView::OnUpdateToolPan(CCmdUI *pCmdUI)
{
	pCmdUI->Enable(!IsBenchEmpty());
	pCmdUI->SetCheck(GetActiveTool() == GetPanTool());
	pCmdUI->SetText("Hand (H)");
}
